package com.markby.fscm.cryptogram.domain.standard0009;

import com.markby.fscm.cryptogram.uitls.HexUtils;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bouncycastle.asn1.*;

import java.math.BigInteger;

/**
 * 0009规范 SM2密文格式
 * SM2Cipher ::= SEQUENCE{
 * xCoordinate		INTEGER,	            --X坐标
 * yCoordinate		INTEGER,	            --Y坐标
 * hash			    OCTET STRING SIZE(32),	--哈希值
 * cipherText		OCTET STRING	        --密文
 * }
 */
@EqualsAndHashCode(callSuper = true)
@Data
public class SM2Cipher extends ASN1Object {

    ASN1Integer xCoordinate;
    ASN1Integer yCoordinate;
    ASN1OctetString hash;
    ASN1OctetString cipherText;

    public SM2Cipher(byte[] encryptDataWithSM2) {
        if (encryptDataWithSM2.length != 113) {
            throw new IllegalArgumentException("SM2Cipher length must be 113");
        }
        BigInteger bi = new BigInteger(1, encryptDataWithSM2);
        String hexString = bi.toString(16);
        System.out.println("对称密钥密文:" + hexString);
        System.out.println("对称密钥密文长度:" + encryptDataWithSM2.length);
        System.out.println("对称密钥密文:" + HexUtils.hexify_haveSpace(encryptDataWithSM2));

        byte[] x = new byte[32];//x分量
        byte[] y = new byte[32];//y分量
        byte[] m = new byte[32];//杂凑值
        byte[] c = new byte[16];//密文
        System.arraycopy(encryptDataWithSM2, 1, x, 0, 32);
        System.arraycopy(encryptDataWithSM2, 33, y, 0, 32);
        System.arraycopy(encryptDataWithSM2, 65, m, 0, 32);
        System.arraycopy(encryptDataWithSM2, 97, c, 0, 16);
        // 如果x，y的二进制最高位为1，则需要补0
        byte[] xBytes = ensureLeadingZero(x);
        byte[] yBytes = ensureLeadingZero(y);


        this.xCoordinate = new ASN1Integer(xBytes);
        this.yCoordinate = new ASN1Integer(yBytes);
        this.hash = new DEROctetString(m);
        this.cipherText = new DEROctetString(c);
    }

    private SM2Cipher(ASN1Sequence sequence) {
        if (sequence.size() != 4) {
            throw new IllegalArgumentException("Invalid SM2Cipher sequence size: " + sequence.size());
        }
        this.xCoordinate = ASN1Integer.getInstance(sequence.getObjectAt(0));
        this.yCoordinate = ASN1Integer.getInstance(sequence.getObjectAt(1));
        this.hash = DEROctetString.getInstance(sequence.getObjectAt(2));
        this.cipherText = DEROctetString.getInstance(sequence.getObjectAt(3));
    }

    public static SM2Cipher getInstance(Object obj) {
        if (obj instanceof SM2Cipher) {
            return (SM2Cipher) obj;
        } else if (obj != null) {
            return new SM2Cipher(ASN1Sequence.getInstance(obj));

        }
        return null;
    }

    @Override
    public ASN1Primitive toASN1Primitive() {
        ASN1EncodableVector vector = new ASN1EncodableVector(4);
        vector.add(xCoordinate);
        vector.add(yCoordinate);
        vector.add(hash);
        vector.add(cipherText);
        return new DERSequence(vector);
    }


    public byte[] getBytes() {
        byte[] xBytes = this.xCoordinate.getValue().toByteArray();
        byte[] yBytes = this.yCoordinate.getValue().toByteArray();
        byte[] mBytes = this.hash.getOctets();
        byte[] cBytes = this.cipherText.getOctets();

        if (mBytes.length != 32) {
            throw new IllegalArgumentException("SM2Cipher-m length must be 32");
        }
        if (cBytes.length != 16) {
            throw new IllegalArgumentException("SM2Cipher-c length must be 16");
        }

        // 合并x，y，m，c
//        int length = xBytes.length + yBytes.length + mBytes.length + cBytes.length;
        byte[] bytes = new byte[113];

        bytes[0] = 0x04; // 固定值
        System.arraycopy(xBytes, xBytes.length - 32, bytes, 1, 32);
        System.arraycopy(yBytes, yBytes.length - 32, bytes, 32 + 1, 32);
        System.arraycopy(mBytes, 0, bytes, 1 + 32 + 32, mBytes.length);
        System.arraycopy(cBytes, 0, bytes, 1 + 32 + 32 + 32, cBytes.length);
        return bytes;
    }

    private static byte[] ensureLeadingZero(byte[] bytes) {
        // 检查最高位是否为1，如果是，则添加前导零
        if (bytes[0] < 0) { // 负数或最高位为1
            byte[] newBytes = new byte[bytes.length + 1];
            newBytes[0] = 0; // 前导零
            System.arraycopy(bytes, 0, newBytes, 1, bytes.length);
            return newBytes;
        }
        return bytes;
    }
}

